package arithm

import (
	"math/big"
	"testing"
)

// TestBigIntegerAddWithInteger tests BigInteger.ADD method with Integer values
func TestBigIntegerAddWithInteger(t *testing.T) {
	// Test normal addition
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Integer(123)
	result := x.ADD(y)

	// When adding BigInteger with Integer, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Add(bigVal, big.NewInt(123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test addition with negative Integer
	bigVal, _ = (new(big.Int)).SetString("12345678901234567890", 10)
	x = (*BigInteger)(bigVal)
	y = Integer(-123)
	result = x.ADD(y)

	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig = new(big.Int).Add(bigVal, big.NewInt(-123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerAddWithBigInteger tests BigInteger.ADD method with BigInteger values
func TestBigIntegerAddWithBigInteger(t *testing.T) {
	// Test addition with another BigInteger
	bigVal1, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	bigVal2, _ := (new(big.Int)).SetString("98765432109876543210", 10)
	x := (*BigInteger)(bigVal1)
	y := (*BigInteger)(bigVal2)
	result := x.ADD(y)

	// When adding BigInteger with BigInteger, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Add(bigVal1, bigVal2)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerCanBeConvertedToInteger tests that BigInteger can be converted to Integer when in range
func TestBigIntegerCanBeConvertedToInteger(t *testing.T) {
	// Test that result is converted to Integer when in int64 range
	bigVal, _ := (new(big.Int)).SetString("123", 10)
	x := (*BigInteger)(bigVal)
	y := Integer(456)
	result := x.ADD(y)

	// When adding two small BigIntegers, result should be converted to Integer
	if result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", INTEGER_TYPE, result.Type())
	}

	expected := Integer(579) // 123 + 456
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerAddWithRational tests BigInteger.ADD method with Rational values
func TestBigIntegerAddWithRational(t *testing.T) {
	// Test addition with Rational
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.ADD(y)

	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	// Create expected result: bigVal + 7/2
	ratBig := new(big.Rat).SetFrac(bigVal, big.NewInt(1))
	expectedRat := new(big.Rat).Add(ratBig, ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestBigIntegerAddWithReal tests BigInteger.ADD method with Real values
func TestBigIntegerAddWithReal(t *testing.T) {
	// Test addition with Real
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Real(3.14)
	result := x.ADD(y)

	// When adding BigInteger with Real, it should delegate to Real.ADD
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Real(f + 3.14)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerAddWithComplex tests BigInteger.ADD method with Complex values
func TestBigIntegerAddWithComplex(t *testing.T) {
	// Test addition with Complex
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Complex(3 + 2i)
	result := x.ADD(y)

	// When adding BigInteger with Complex, it should delegate to Complex.ADD
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Complex(complex(f, 0) + complex128(y))
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerSubWithInteger tests BigInteger.SUB method with Integer values
func TestBigIntegerSubWithInteger(t *testing.T) {
	// Test normal subtraction
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Integer(123)
	result := x.SUB(y)

	// When subtracting Integer from BigInteger, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Sub(bigVal, big.NewInt(123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test subtraction with negative Integer
	bigVal, _ = (new(big.Int)).SetString("12345678901234567890", 10)
	x = (*BigInteger)(bigVal)
	y = Integer(-123)
	result = x.SUB(y)

	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig = new(big.Int).Sub(bigVal, big.NewInt(-123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerSubWithBigInteger tests BigInteger.SUB method with BigInteger values
func TestBigIntegerSubWithBigInteger(t *testing.T) {
	// Test subtraction with another BigInteger
	bigVal1, _ := (new(big.Int)).SetString("98765432109876543210", 10)
	bigVal2, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal1)
	y := (*BigInteger)(bigVal2)
	result := x.SUB(y)

	// When subtracting BigInteger from BigInteger, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Sub(bigVal1, bigVal2)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerSubWithRational tests BigInteger.SUB method with Rational values
func TestBigIntegerSubWithRational(t *testing.T) {
	// Test subtraction with Rational
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.SUB(y)

	// When subtracting Rational from BigInteger, it should delegate to Rational.ADD with negated value
	// which should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	// Create expected result: bigVal - 7/2
	ratBig := new(big.Rat).SetFrac(bigVal, big.NewInt(1))
	expectedRat := new(big.Rat).Sub(ratBig, ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestBigIntegerSubWithReal tests BigInteger.SUB method with Real values
func TestBigIntegerSubWithReal(t *testing.T) {
	// Test subtraction with Real
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Real(3.14)
	result := x.SUB(y)

	// When subtracting Real from BigInteger, it should delegate to Real.ADD with negated value
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Real(f - 3.14)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerSubWithComplex tests BigInteger.SUB method with Complex values
func TestBigIntegerSubWithComplex(t *testing.T) {
	// Test subtraction with Complex
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Complex(3 + 2i)
	result := x.SUB(y)

	// When subtracting Complex from BigInteger, it should delegate to Complex.ADD with negated value
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Complex(complex(f, 0) - complex128(y))
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerMulWithInteger tests BigInteger.MUL method with Integer values
func TestBigIntegerMulWithInteger(t *testing.T) {
	// Test normal multiplication
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Integer(123)
	result := x.MUL(y)

	// When multiplying BigInteger with Integer, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Mul(bigVal, big.NewInt(123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test multiplication with negative Integer
	bigVal, _ = (new(big.Int)).SetString("12345678901234567890", 10)
	x = (*BigInteger)(bigVal)
	y = Integer(-123)
	result = x.MUL(y)

	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig = new(big.Int).Mul(bigVal, big.NewInt(-123))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test multiplication by zero
	bigVal, _ = (new(big.Int)).SetString("12345678901234567890", 10)
	x = (*BigInteger)(bigVal)
	y = Integer(0)
	result = x.MUL(y)

	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig = new(big.Int).Mul(bigVal, big.NewInt(0))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerMulWithBigInteger tests BigInteger.MUL method with BigInteger values
func TestBigIntegerMulWithBigInteger(t *testing.T) {
	// Test multiplication with another BigInteger
	bigVal1, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	bigVal2, _ := (new(big.Int)).SetString("98765432109876543210", 10)
	x := (*BigInteger)(bigVal1)
	y := (*BigInteger)(bigVal2)
	result := x.MUL(y)

	// When multiplying BigInteger with BigInteger, it should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Mul(bigVal1, bigVal2)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestBigIntegerMulWithRational tests BigInteger.MUL method with Rational values
func TestBigIntegerMulWithRational(t *testing.T) {
	// Test multiplication with Rational that results in a non-integer
	// Using an odd number and 1/2 ensures we get a fractional result (n/2 where n is odd)
	bigVal, _ := (new(big.Int)).SetString("12345678901234567891", 10) // Odd number
	x := (*BigInteger)(bigVal)
	ratVal := big.NewRat(1, 2) // 1/2
	y := (*Rational)(ratVal)
	result := x.MUL(y)

	// When multiplying BigInteger with Rational, it should delegate to Rational.MUL
	// which should return a Rational since the result is not a whole number
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	// Create expected result: bigVal * 1/2
	ratBig := new(big.Rat).SetFrac(bigVal, big.NewInt(1))
	expectedRat := new(big.Rat).Mul(ratBig, ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestBigIntegerMulWithReal tests BigInteger.MUL method with Real values
func TestBigIntegerMulWithReal(t *testing.T) {
	// Test multiplication with Real
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Real(3.14)
	result := x.MUL(y)

	// When multiplying BigInteger with Real, it should delegate to Real.MUL
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Real(f * 3.14)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerMulWithComplex tests BigInteger.MUL method with Complex values
func TestBigIntegerMulWithComplex(t *testing.T) {
	// Test multiplication with Complex
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Complex(3 + 2i)
	result := x.MUL(y)

	// When multiplying BigInteger with Complex, it should delegate to Complex.MUL
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Complex(complex(f, 0) * complex128(y))
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerDivWithInteger tests BigInteger.DIV method with Integer values
func TestBigIntegerDivWithInteger(t *testing.T) {
	// Test division that results in an integer
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Integer(2)
	result := x.DIV(y)

	// When dividing BigInteger by Integer that results in a whole number,
	// it should return a BigInteger or Integer depending on size
	if result.Type() != BIGINTEGER_TYPE && result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v or %v, got %v", BIGINTEGER_TYPE, INTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Div(bigVal, big.NewInt(2))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test division that results in a rational
	bigVal2, _ := (new(big.Int)).SetString("12345678901234567891", 10) // Odd number
	x2 := (*BigInteger)(bigVal2)
	y2 := Integer(2)
	result2 := x2.DIV(y2)

	// When dividing BigInteger by Integer that does not result in a whole number,
	// it should return a Rational
	if result2.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result2.Type())
	}

	expectedRat := new(big.Rat).SetFrac(bigVal2, big.NewInt(2))
	if result2.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result2)
	}

	// Test division with negative Integer
	bigVal3, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x3 := (*BigInteger)(bigVal3)
	y3 := Integer(-2)
	result3 := x3.DIV(y3)

	// Result type could be BigInteger or Integer depending on size
	if result3.Type() != BIGINTEGER_TYPE && result3.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v or %v, got %v", BIGINTEGER_TYPE, INTEGER_TYPE, result3.Type())
	}

	expectedBig3 := new(big.Int).Div(bigVal3, big.NewInt(-2))
	if result3.String() != expectedBig3.String() {
		t.Errorf("Expected %v, got %v", expectedBig3, result3)
	}
}

// TestBigIntegerDivWithBigInteger tests BigInteger.DIV method with BigInteger values
func TestBigIntegerDivWithBigInteger(t *testing.T) {
	// Test division with another BigInteger that results in an integer
	bigVal1, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	bigVal2, _ := (new(big.Int)).SetString("2", 10)
	x := (*BigInteger)(bigVal1)
	y := (*BigInteger)(bigVal2)
	result := x.DIV(y)

	// When dividing BigInteger by BigInteger that results in a whole number,
	// it should return a BigInteger or Integer depending on size
	if result.Type() != BIGINTEGER_TYPE && result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v or %v, got %v", BIGINTEGER_TYPE, INTEGER_TYPE, result.Type())
	}

	expectedRat := new(big.Rat).SetFrac(bigVal1, bigVal2)
	expectedBig := expectedRat.Num() // Since it's a whole number, numerator is the result
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test division with another BigInteger that results in a rational
	bigVal3, _ := (new(big.Int)).SetString("12345678901234567891", 10) // Odd number
	bigVal4, _ := (new(big.Int)).SetString("2", 10)
	x2 := (*BigInteger)(bigVal3)
	y2 := (*BigInteger)(bigVal4)
	result2 := x2.DIV(y2)

	// When dividing BigInteger by BigInteger that does not result in a whole number,
	// it should return a Rational
	if result2.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result2.Type())
	}

	expectedRat2 := new(big.Rat).SetFrac(bigVal3, bigVal4)
	if result2.String() != expectedRat2.String() {
		t.Errorf("Expected %v, got %v", expectedRat2, result2)
	}
}

// TestBigIntegerDivWithRational tests BigInteger.DIV method with Rational values
func TestBigIntegerDivWithRational(t *testing.T) {
	// Test division with Rational
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	x := (*BigInteger)(bigVal)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.DIV(y)

	// When dividing BigInteger by Rational, it should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	// Create expected result: bigVal / 7/2 = bigVal * 2/7
	ratBig := new(big.Rat).SetFrac(bigVal, big.NewInt(1))
	expectedRat := new(big.Rat).Quo(ratBig, ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestBigIntegerDivWithReal tests BigInteger.DIV method with Real values
func TestBigIntegerDivWithReal(t *testing.T) {
	// Test division with Real
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Real(3.14)
	result := x.DIV(y)

	// When dividing BigInteger by Real, it should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Real(f / 3.14)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestBigIntegerDivWithComplex tests BigInteger.DIV method with Complex values
func TestBigIntegerDivWithComplex(t *testing.T) {
	// Test division with Complex
	bigVal, _ := (new(big.Int)).SetString("1234567890", 10)
	x := (*BigInteger)(bigVal)
	y := Complex(3 + 2i)
	result := x.DIV(y)

	// When dividing BigInteger by Complex, it should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	f, _ := bigVal.Float64()
	expected := Complex(complex(f, 0) / complex128(y))
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestRationalAddWithInteger tests Rational.ADD method with Integer values
